#include "poorerwater.h"
#include "datamanager.h"
#include "resourceloader.h"
#include "skydome.h"
#include "datatypedefine.h"

static const char poorocena_vertex[] = 
"const float Eta = 0.752;\n"
"const float FresnelPower = 5.0;\n"
"const float F = ((1.0-Eta) * (1.0-Eta))/((1.0+Eta) * (1.0+Eta));\n"
"varying float FresRatio;\n"
"varying vec3 lightDir; \n"
"varying vec3 eyeDir;\n"
"varying vec3 reflectDir;\n"
"varying vec3 refractDir;\n"
"attribute vec3 Tangent;\n"
"uniform vec3 LightPos;\n"
"uniform float time;\n"
"void main(void)\n"
"{\n"
	"gl_Position = ftransform();\n"
	"vec4 v = vec4(gl_Vertex);\n"
	"vec3 norm = normalize(gl_NormalMatrix * gl_Normal);\n"
	"vec3 tang = normalize(gl_NormalMatrix * Tangent);\n"
	"vec3 binorm = cross(norm,tang);\n"
	"eyeDir = vec3(gl_ModelViewMatrix * v);\n"
	"//for refraction\n"
	"vec4 neyeDir = gl_ModelViewMatrix * v;\n"
	"vec3 refeyeDir = neyeDir.xyz / neyeDir.w;\n"
	"refeyeDir = normalize(refeyeDir);\n"
	"reflectDir = reflect(eyeDir,norm);\n"
	"refractDir = refract(eyeDir,norm,Eta);\n"
	"refractDir = vec3(gl_TextureMatrix[0] * vec4(refractDir,1.0));\n"
	"FresRatio = F + (1.0-F) * pow((1.0-dot(-refeyeDir,norm)),FresnelPower);\n"
	"vec3 tmp;\n"
	"tmp.x = dot(LightPos,tang);\n"
	"tmp.y = dot(LightPos,binorm);\n"
	"tmp.z = dot(LightPos,norm);\n"
	"lightDir = normalize(tmp);\n"
	"tmp.x = dot(-eyeDir,tang);\n"
	"tmp.y = dot(-eyeDir,binorm);\n"
	"tmp.z = dot(-eyeDir,norm);\n"
	"eyeDir = normalize(tmp);\n"
	"gl_TexCoord[0] = gl_MultiTexCoord0;\n"
	"vec4 texco = gl_MultiTexCoord0;\n"
	"texco.s = texco.s + time*0.01;\n"
	"texco.t = texco.t + time*0.01;\n"
	"gl_TexCoord[1] = texco;\n"
	"texco = gl_MultiTexCoord0;\n"
	"texco.s = texco.s + time*0.01;\n"
	"texco.t = texco.t + -time*0.01;\n"
	"gl_TexCoord[2] = texco;\n"
	"gl_TexCoord[3] = gl_MultiTexCoord2;\n"
"}\n";

static const char poorocena_fragment[] = 
"varying vec3 lightDir;\n"
"varying vec3 eyeDir;\n"
"varying vec3 reflectDir;\n"
"varying vec3 refractDir;\n"
"varying float FresRatio;\n"
"uniform sampler2D normalMap;\n"
"uniform sampler2D normalMapbase;\n"
"uniform samplerCube cubeMap;\n"
"void main (void)\n"
"{\n"
"float distSqr = dot(lightDir, lightDir);\n"
"float att = clamp(1.0 - 0.0 * sqrt(distSqr), 0.0, 1.0);\n"
"vec3 lVec = lightDir * inversesqrt(distSqr);\n"
"vec3 vVec = normalize(eyeDir);\n"
"vec4 base = vec4(0.17,0.67,0.93,1.0); //base water color\n"
"vec3 bump = normalize( texture2D(normalMap, gl_TexCoord[1].st).xyz * 2.0 - 1.0);\n"
"vec3 secbump = normalize( texture2D(normalMap, gl_TexCoord[2].st).xyz * 2.0 - 1.0);\n"
"vec3 thrbump = normalize( texture2D(normalMapbase, gl_TexCoord[3].st).xyz * 2.0 - 1.0);\n"
"vec3 modbump = mix(secbump,bump,0.5);\n"
"modbump = mix(thrbump,modbump,0.5);\n"
"vec3 reflection = vec3(textureCube(cubeMap,reflect(eyeDir,modbump)));\n"
"vec3 refraction = vec3(textureCube(cubeMap,refract(eyeDir,modbump,0.66)));\n"
"vec3 cubemap = mix(reflection,refraction,FresRatio);\n"
"vec4 vAmbient = gl_LightSource[0].ambient;\n"
"float diffuse = max( dot(-lVec, modbump), 0.0 );\n"
"vec4 vDiffuse = gl_LightSource[0].diffuse * gl_FrontMaterial.diffuse * diffuse;\n"
"float specular = pow(clamp(dot(reflect(lVec, modbump), vVec), 0.0, 1.0), 0.5 );\n"
"vec4 vSpecular = gl_LightSource[0].specular * gl_FrontMaterial.specular * specular;\n"
"vec4 newfrag = mix(vec4(cubemap,1.0) , (( vAmbient*base + vDiffuse*base * vSpecular*base ) * att),0.5);\n"
//"gl_FragColor = mix(vec4(cubemap,1.0),base,0.7) ; //newfrag;\n"
"gl_FragColor = newfrag ; //newfrag;\n"
"gl_FragColor.a = 0.5f; //newfrag;\n"
"}\n";

namespace VrDataCore
{
	PoorerWater::PoorerWater(CoreBase::ActorBase* owneractor,const QString& name):CoreBase::Transformable(owneractor,name),_size(129,129)
		,_inScene(false),_root(NULL),_skyTextureCubeMap(NULL)
	{}

	PoorerWater::~PoorerWater()
	{}

	void PoorerWater::setSize(const osg::Vec2& size)
	{
		_size = size;
		if(_inScene)
		{
			getOSGNode()->asGroup()->removeChild(_root);
			_root = NULL;
			build();
		}
	}

	void PoorerWater::addedToScene(CoreBase::Scene* scene)
	{
		if(scene!=NULL)
		{
			_inScene = true;
			if(!_root.valid())
				build();
		}
		else
			_inScene = false;
		CoreBase::Transformable::addedToScene(scene);	
	}

	void PoorerWater::build()
	{
		_root = new osg::Group;
		_planeShape = new CoreBase::PlaneShape;
		float xsize = _size.x();
		float ysize = _size.y();
		osg::Vec3 p1(-xsize/2.0f,-ysize/2.0f,0.0f);
		osg::Vec3 p2(xsize/2.0f,-ysize/2.0f,0.0f);
		osg::Vec3 p3(xsize/2.0f,ysize/2.0f,0.0f);
		osg::Vec3 p4(-xsize/2.0f,ysize/2.0f,0.0f);
		_ss = VrDataCore::DataManager::GetInstance().getPublicStateSetByDrawableName("\\scenetextures\\skydome\\water.jpg");
		osg::ref_ptr<osg::Shader> vertexShader = new osg::Shader( osg::Shader::VERTEX,poorocena_vertex);
		osg::ref_ptr<osg::Shader> fragmentShader = new osg::Shader( osg::Shader::FRAGMENT,poorocena_fragment);
		osg::ref_ptr<osg::Program> program = new osg::Program;
		program->setName("water program");
		program->addShader( vertexShader.get());
		program->addShader( fragmentShader.get());
		_ss->setAttributeAndModes( program.get(), osg::StateAttribute::ON );
		osg::Uniform* param1 = new osg::Uniform("LightPos", osg::Vec3(0.0, 0.0, 1.0) );
		_ss->addUniform( param1 );
		osg::Uniform* param2 = new osg::Uniform("time",0.0f );
		param2->setUpdateCallback( new UpdateShader );
		_ss->addUniform( param2 );
		osg::Uniform* param3 = new osg::Uniform("normalMap",0);
		_ss->addUniform( param3 );
		osg::Uniform* param4 = new osg::Uniform("cubeMap",1);
		_ss->addUniform( param4 );
		osg::Uniform* param5 = new osg::Uniform("normalMapbase",2);
		_ss->addUniform( param5 );
		osg::ref_ptr<osg::Image> imagepath = CoreBase::ResourceLoader::GetInstance().getImageByFileName("\\scenetextures\\skydome\\water.jpg");
		osg::ref_ptr<osg::Image> basewater = CoreBase::ResourceLoader::GetInstance().getImageByFileName("\\scenetextures\\skydome\\waterbase.jpg");
		if(imagepath.valid() && basewater.valid())
		{
			osg::Texture2D* tex2d = new osg::Texture2D;
			tex2d->setImage(imagepath);
			tex2d->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
			tex2d->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
			tex2d->setWrap(osg::Texture::WRAP_R, osg::Texture::REPEAT);
			tex2d->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
			tex2d->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
			_ss->setTextureAttributeAndModes(0,tex2d,osg::StateAttribute::ON);
			_ss->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
			_ss->setTextureAttributeAndModes(1,_skyTextureCubeMap,osg::StateAttribute::ON);
			osg::Texture2D* waterbasetex = new osg::Texture2D;
			waterbasetex->setImage(basewater);
			waterbasetex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
			waterbasetex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
			waterbasetex->setWrap(osg::Texture::WRAP_R, osg::Texture::REPEAT);
			waterbasetex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
			waterbasetex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
			_ss->setTextureAttributeAndModes(2,waterbasetex,osg::StateAttribute::ON);
		}
		_planeShape->reset(p1,p2,p3,p4,osg::Vec2(0.0f,0.0f),osg::Vec2(xsize/51.20f,0.0f),
				osg::Vec2(xsize/51.20f,ysize/51.20f),osg::Vec2(0.0f,ysize/51.20f),_ss);
		_root->addChild(_planeShape->getOSGNode());
		_ss->setMode(GL_BLEND,osg::StateAttribute::ON|osg::StateAttribute::PROTECTED);
		_ss->setRenderBinDetails(10000, "DepthSortedBin");
		//osg::LightSource* lightSource = new osg::LightSource;
		//lightSource->setLocalStateSetModes();
		//_light = lightSource->getLight();
		//_light->setLightNum(0);
		//_light->setAmbient( osg::Vec4d(0.3f, 0.3f, 0.3f, 1.0f ));
		//_light->setDiffuse( sundiffuse );
		//_light->setSpecular(osg::Vec4d( 0.1f, 0.1f, 0.1f, 1.0f ) );
		//_light->setPosition( osg::Vec4f(sunpositions, 1.f) ); // point light
		//_root->addChild( lightSource );
		//_root->addChild( _oceanScene.get());
		getOSGNode()->asGroup()->addChild(_root);
	}

	bool PoorerWater::addChild(CoreBase::Drawable* child)
	{
		if(dynamic_cast<SkyDome*>(child)!=NULL)
		{
			_root->addChild(child->getOSGNode());
			//child->getOSGNode()->setNodeMask( _oceanScene->getReflectedSceneMask() | _oceanScene->getNormalSceneMask() );
		}
		return true;
	}

	void PoorerWater::setEnvironmentParameter(osg::TextureCubeMap* texture,const osg::Vec4& lightcolor,const osg::Vec4& fogcolors,
			const osg::Vec4& waterfogcolors,const osg::Vec4& underwaterdiffuse,const osg::Vec3& underwaterattenuations
			,const osg::Vec3& sunpositions,const osg::Vec4& sundiffuse)
	{
		if(texture!=NULL && texture!=_skyTextureCubeMap)
		{
			_skyTextureCubeMap = texture;
			if(_ss.valid())
				_ss->setTextureAttributeAndModes(1,_skyTextureCubeMap,osg::StateAttribute::ON);
		}
	}
}